{{!

  Copyright (c) Facebook, Inc. and its affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}{{!

Generates a top-level file to be imported in the user's client code.

The generated file is pretty big, but the bulk of the generation is done inside
the clients/Callbacks partial.

}}
{{> common/AutoGeneratedPy}}
from libc.stdint cimport (
    int8_t as cint8_t,
    int16_t as cint16_t,
    int32_t as cint32_t,
    int64_t as cint64_t,
)
from libcpp.memory cimport shared_ptr, make_shared, unique_ptr, make_unique
from libcpp.string cimport string
from libcpp cimport bool as cbool
from cpython cimport bool as pbool
from libcpp.vector cimport vector as vector
from libcpp.set cimport set as cset
from libcpp.map cimport map as cmap
from cython.operator cimport dereference as deref, typeid
from cpython.ref cimport PyObject
from thrift.py3.client cimport cRequestChannel_ptr, makeClientWrapper, cClientWrapper
from thrift.py3.exceptions cimport try_make_shared_exception, create_py_exception
from folly cimport cFollyTry, cFollyUnit, c_unit
from folly.cast cimport down_cast_ptr
from libcpp.typeinfo cimport type_info
import thrift.py3.types
cimport thrift.py3.types
from thrift.py3.types cimport move
from thrift.py3.types import NumberType as __NumberType
import thrift.py3.client
cimport thrift.py3.client
from thrift.py3.common cimport RpcOptions as __RpcOptions
from thrift.py3.common import (
  RpcOptions as __RpcOptions,
  InterfaceSpec as __InterfaceSpec,
  MethodSpec as __MethodSpec,
  ArgumentSpec as __ArgumentSpec,
)

from folly.futures cimport bridgeFutureWith
from folly.executor cimport get_executor
cimport folly.iobuf as __iobuf
import folly.iobuf as __iobuf
from folly.iobuf cimport move as move_iobuf
cimport cython

import sys
import types as _py_types
from asyncio import get_event_loop as asyncio_get_event_loop, shield as asyncio_shield, InvalidStateError as asyncio_InvalidStateError

cimport {{#program:py3Namespaces}}{{value}}.{{/program:py3Namespaces}}{{program:name}}.types as _{{#program:py3Namespaces}}{{value}}_{{/program:py3Namespaces}}{{program:name}}_types
import {{#program:py3Namespaces}}{{value}}.{{/program:py3Namespaces}}{{program:name}}.types as _{{#program:py3Namespaces}}{{value}}_{{/program:py3Namespaces}}{{program:name}}_types
{{#program:has_stream?}}
from thrift.py3.stream cimport cResponseAndClientBufferedStream, cClientBufferedStream
{{/program:has_stream?}}
{{#program:includeNamespaces}}
{{#hasTypes?}}
cimport {{#includeNamespace}}{{value}}.{{/includeNamespace}}types as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}types
import {{#includeNamespace}}{{value}}.{{/includeNamespace}}types as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}types
{{/hasTypes?}}
{{#hasServices?}}
cimport {{#includeNamespace}}{{value}}.{{/includeNamespace}}clients as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}clients
import {{#includeNamespace}}{{value}}.{{/includeNamespace}}clients as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}clients
{{/hasServices?}}
{{/program:includeNamespaces}}

{{#program:services}}
from {{#program:py3Namespaces}}{{value}}.{{/program:py3Namespaces}}{{program:name}}.clients_wrapper cimport c{{service:name}}AsyncClient, c{{service:name}}ClientWrapper
{{#service:extends}}
{{#service:externalProgram?}}
from {{#service:py3Namespaces}}{{value}}.{{/service:py3Namespaces}}{{service:programName}}.clients_wrapper cimport c{{service:name}}ClientWrapper
{{/service:externalProgram?}}
{{/service:extends}}
{{/program:services}}

{{> clients/Callbacks}}

{{#program:services}}
cdef object _{{service:name}}_annotations = _py_types.MappingProxyType({
{{#service:annotations}}
    {{> Annotation}}
    {{#last?}}

    {{/last?}}
{{/service:annotations}}
})


@cython.auto_pickle(False)
cdef class {{service:name}}({{#service:extends}}{{#service:externalProgram?}}{{!
    }}_{{#service:py3Namespaces}}{{value}}_{{/service:py3Namespaces}}{{!
    }}{{service:programName}}_clients.{{/service:externalProgram?}}{{service:name}}{{!
  }}{{/service:extends}}{{^service:extends?}}thrift.py3.client.Client{{/service:extends?}}):
    annotations = _{{service:name}}_annotations

    cdef const type_info* _typeid({{service:name}} self):
        return &typeid(c{{service:name}}AsyncClient)

    cdef bind_client({{service:name}} self, cRequestChannel_ptr&& channel):
        self._client = makeClientWrapper[c{{service:name}}AsyncClient, c{{service:name}}ClientWrapper](
            thrift.py3.client.move(channel)
        )

{{#service:functions}}{{^function:returns_sink?}}
    @cython.always_allow_keywords(True)
    def {{function:name}}(
            {{service:name}} self{{#function:args}},
            {{#field:type}}{{!
                }}{{#type:hasCythonType?}}{{^type:integer?}}{{> types/CythonPythonType}} {{/type:integer?}}{{/type:hasCythonType?}}{{!
                }}{{field:py_name}}{{#type:integer?}} not None{{/type:integer?}}{{^type:number?}} not None{{/type:number?}}{{!
            }}{{/field:type}}{{/function:args}},
            __RpcOptions rpc_options=None
    ):
        if rpc_options is None:
            rpc_options = <__RpcOptions>__RpcOptions.__new__(__RpcOptions)
        {{#function:args}}
        {{#field:type}}
        {{#type:container?}}
        if not isinstance({{field:py_name}}, {{> types/PythonType}}):
            {{field:py_name}} = {{> types/PythonType}}({{field:py_name}})
        {{/type:container?}}
        {{#type:integer?}}
        if not isinstance({{field:py_name}}, int):
            raise TypeError(f'{{field:py_name}} is not a {int !r}.')
        else:
            {{! inject cython int Overflow checks }}
            {{field:py_name}} = <{{> types/CythonPythonType}}> {{field:py_name}}
        {{/type:integer?}}
        {{/field:type}}
        {{/function:args}}
        self._check_connect_future()
        __loop = asyncio_get_event_loop()
        __future = __loop.create_future()
        __userdata = (self, __future, rpc_options)
        bridgeFutureWith[{{#function:returnType}}{{> clients/CythonReturnTypeCppType}}{{/function:returnType}}](
            self._executor,
            down_cast_ptr[c{{service:name}}ClientWrapper, cClientWrapper](self._client.get()).{{function:name}}(rpc_options._cpp_obj, {{#function:args}}
                {{#field:type}}{{> CythonPythonToCppArg}}{{/field:type}},{{/function:args}}
            ),
            {{service:name}}_{{function:name}}_callback,
            <PyObject *> __userdata
        )
        return asyncio_shield(__future)

{{/function:returns_sink?}}{{/service:functions}}
{{#service:functions}}{{^function:returns_sink?}}

    @staticmethod
    def __get_reflection_for_{{function:name}}():
        return __MethodSpec(
            name="{{function:name}}",
            arguments=[
                {{#function:args}}
                __ArgumentSpec(
                    name="{{field:name}}",
                    type={{#field:type}}{{> types/PythonType}}{{/field:type}},
                    kind={{#field:type}}{{> types/ThriftNumberType}}{{/field:type}},
                    annotations=_py_types.MappingProxyType({
                        {{#field:annotations}}
                        {{> Annotation }}
                        {{#last?}}

                        {{/last?}}
                        {{/field:annotations}}
                    }),
                ),
                {{/function:args}}
            ],
            result={{#function:returnType}}{{!
                }}{{#type:void?}}None{{/type:void?}}{{!
                }}{{#type:streamresponse?}}{{> clients/StreamResponseClassName}}{{/type:streamresponse?}}{{!
                }}{{^type:void?}}{{> types/PythonType}}{{/type:void?}}{{!
                }}{{/function:returnType}},
            result_kind={{#function:returnType}}{{> types/ThriftNumberType}}{{/function:returnType}},
            exceptions=[
                {{#function:exceptions}}
                {{#field:type}}{{> types/PythonType}}{{/field:type}},
                {{/function:exceptions}}
            ],
            annotations=_py_types.MappingProxyType({
                {{#function:annotations}}
                {{> Annotation }}
                {{#last?}}

                {{/last?}}
                {{/function:annotations}}
            }),
        )
{{/function:returns_sink?}}{{/service:functions}}

    {{> reflection/GetReflectionMethod}}


{{/program:services}}
